﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;

namespace myStuff
{
    public partial class ImageAnnotator : UserControl
    {
        Bitmap bitmap;
        int controlId = -1, penWidth = 3;
        bool dragging = false;
        Color thingColor = Color.Red;

        List<Thing> things = new List<Thing>();

        Thing tempThing = null;

        const int idNone = -1, idCrop = 0, idEllipse = 1, idText = 2, idArrow = 3, idLine = 4, idRect = 5;
        Graphics g; Pen pen; int x, y;

        string filename;
        string nextFilename;
        public Delegate userFunctionPointerSize;
        public Delegate userFunctionPointerClose;
        public bool bSaveOnClose = true;
        //float resolution = 300;

        public ImageAnnotator()
        {
            
            InitializeComponent();
            controlId = idCrop;
            toolStripButtonCrop.Checked = true;
            thingColor = Color.Red;
            toolStripButtonRed.Checked = true;
        }

        public void setFile(string _filename, string _nextFilename)
        {
            //if (_filename == null) _filename = "";
            filename = _filename;
            nextFilename = _nextFilename;
            //pictureBox1.ImageLocation = _filename;
            
            pictureBox1.Image = new Bitmap(_filename);
            //pictureBox1.Refresh();
            bitmap = new Bitmap(pictureBox1.Image);
            userFunctionPointerSize.DynamicInvoke(pictureBox1.Image.Size);
        }

        public bool pasteImage(string _filename)
        {
            //pictureBox1.ImageLocation = filename;
            //filename = _filename;
            nextFilename = _filename;
            pictureBox1.Image = Clipboard.GetImage();
            
            if (pictureBox1.Image == null)
                return false; //error

            bitmap = new Bitmap(pictureBox1.Image);
            userFunctionPointerSize.DynamicInvoke(pictureBox1.Image.Size);
            //bitmap.SetResolution(resolution, resolution);
            
            return true;
        }

        public Size getImageSize()
        {
            return pictureBox1.Image.Size;
        }

        private void toolStripButtonEllipse_Click(object sender, EventArgs e)
        {
            controlId = idEllipse;
            toolStripButtonEllipse.Checked = true;
            toolStripButtonRect.Checked = false;
            toolStripButtonLine.Checked = false;
            toolStripButtonArrow.Checked = false;
            toolStripButtonText.Checked = false;
            toolStripButtonCrop.Checked = false; 
        }

        private void toolStripButtonRect_Click(object sender, EventArgs e)
        {
            controlId = idRect;
            toolStripButtonEllipse.Checked = false;
            toolStripButtonRect.Checked = true;
            toolStripButtonLine.Checked = false;
            toolStripButtonArrow.Checked = false;
            toolStripButtonText.Checked = false;
            toolStripButtonCrop.Checked = false;
        }

        private void toolStripButtonLine_Click(object sender, EventArgs e)
        {
            controlId = idLine;
            toolStripButtonLine.Checked = true;
            toolStripButtonEllipse.Checked = false;
            toolStripButtonRect.Checked = false;
            toolStripButtonArrow.Checked = false;
            toolStripButtonText.Checked = false;
            toolStripButtonCrop.Checked = false;
        }

        private void toolStripButtonArrow_Click(object sender, EventArgs e)
        {
            controlId = idArrow;
            toolStripButtonArrow.Checked = true;
            toolStripButtonEllipse.Checked = false;
            toolStripButtonRect.Checked = false;
            toolStripButtonLine.Checked = false;    
            toolStripButtonText.Checked = false;
            toolStripButtonCrop.Checked = false;
        }

        private void toolStripButtonText_Click(object sender, EventArgs e)
        {
            controlId = idText;
            toolStripButtonText.Checked = true;
            toolStripButtonEllipse.Checked = false;
            toolStripButtonRect.Checked = false;
            toolStripButtonLine.Checked = false;
            toolStripButtonArrow.Checked = false;  
            toolStripButtonCrop.Checked = false;
        }

        private void toolStripButtonCrop_Click(object sender, EventArgs e)
        {
            controlId = idCrop;
            toolStripButtonCrop.Checked = true;
            toolStripButtonEllipse.Checked = false;
            toolStripButtonRect.Checked = false;
            toolStripButtonLine.Checked = false;
            toolStripButtonArrow.Checked = false;
            toolStripButtonText.Checked = false;
         
        }

        private void toolStripButtonRed_Click(object sender, EventArgs e)
        {
            thingColor  = Color.Red;
            toolStripButtonRed.Checked = true;
            toolStripButtonBlue.Checked = false;
            toolStripButtonGreen.Checked = false;
        }

        private void toolStripButtonBlue_Click(object sender, EventArgs e)
        {
            thingColor = Color.Blue;
            toolStripButtonBlue.Checked = true;
            toolStripButtonRed.Checked = false;
            toolStripButtonGreen.Checked = false;
        }

        private void toolStripButtonGreen_Click(object sender, EventArgs e)
        {
            thingColor = Color.Green;
            toolStripButtonGreen.Checked = true;
            toolStripButtonRed.Checked = false;
            toolStripButtonBlue.Checked = false;
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (tempThing != null && tempThing.id == idText)
            {
                //tempThing.txt.BorderStyle = BorderStyle.None;
                things.Add(tempThing);
                tempThing = null;
                //return;
            }
            
            if (controlId != idNone && controlId != idText)
            {
                x = e.X;
                y = e.Y;
                dragging = true;
                tempThing = new Thing(controlId, x, y, 0, 0, thingColor);
            }
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (dragging && controlId != idNone && tempThing != null)
            {
                tempThing.w = e.X - x;
                tempThing.h = e.Y - y;
                redraw();
            }
        }


        private void redraw()
        {
            if (pictureBox1.Image == null || bitmap == null) return;
            Graphics.FromImage(pictureBox1.Image).DrawImageUnscaled(bitmap, 0, 0);
            
            g = Graphics.FromImage(pictureBox1.Image);
            g.SmoothingMode = SmoothingMode.AntiAlias;
            pen = new Pen(thingColor, penWidth);


            foreach (Thing t in things)
            {
                if (t.id != idText)
                    drawThing(t);
            }

            if (tempThing != null && tempThing.id != idText)
               drawThing(tempThing);

            foreach (Thing t in things) //draw text last
            {
                if (t.id == idText)
                    drawThing(t);
            }

            if (tempThing != null && tempThing.id == idText)
                drawThing(tempThing);       

            pictureBox1.Refresh();
            g.Dispose();
        }



        void drawThing(Thing t)
        {
            pen.Color = t.col;
            float o;
            o = (float)penWidth / 2.0f;
            switch (t.id)
            {
                case idCrop:
                    if (tempThing == null)
                        break;
                    Pen p = new Pen(Color.Blue, 2);
                    p.DashStyle = DashStyle.Dash;
                    g.DrawRectangle(p, tempThing.x - o, tempThing.y - o, tempThing.w + penWidth, tempThing.h + penWidth);
                    break;
                case idEllipse:
                    pen.Width = 4;
                    o = (float)penWidth / 2.0f;
                    pen.Color = Color.FromArgb(170, t.col);
                    g.DrawEllipse(pen, t.x, t.y, t.w, t.h);
                    break;
                case idText:
                    pen.Width = 8;
                    o = (float)penWidth / 2.0f;
                    pen.Color = Color.FromArgb(110, t.col);
                    
                    g.FillRectangle(new SolidBrush( Color.White), t.x + o, t.y + o, t.w - penWidth - 1, t.h - penWidth - 1);
                    g.DrawRectangle(pen, t.x - o, t.y - o, t.w + penWidth - 1, t.h + penWidth - 1);
                    g.DrawString(t.txt, new Font("Arial", 9), new SolidBrush(t.col), new PointF(t.x + 4, t.y + 4));
                    break;
                case idArrow:
                    pen.EndCap = LineCap.ArrowAnchor;
                    pen.Color = Color.FromArgb(160, t.col);
                    pen.Width = 12;
                    g.DrawLine(pen, t.x, t.y, t.x + t.w, t.y + t.h);
                    break;
                case idLine:
                    pen.EndCap = LineCap.Flat;
                    pen.Color = Color.FromArgb(170, t.col);
                    pen.Width = 4;
                    o = (float)penWidth / 2.0f;
                    g.DrawLine(pen, t.x, t.y, t.x + t.w, t.y + t.h);
                    break;
                case idRect:
                    pen.Width = 3;
                    o = (float)penWidth / 2.0f;
                    pen.Color = Color.FromArgb(170, t.col);
                    g.DrawRectangle(pen, t.x - o, t.y - o, t.w + penWidth, t.h + penWidth);
                    break;
            }
        }


        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            //if (tempThing == null) return;
            dragging = false;
            if (controlId == idCrop)
                if (e.X - x < 2 || e.Y - y < 2)
                {
                    tempThing = null;
                    return;
                }
                else
                {
                    Graphics g1 = Graphics.FromImage(pictureBox1.Image);
                    
                    Bitmap croppedImage = new Bitmap(e.X - x, e.Y - y, g1);
                    Graphics g2 = Graphics.FromImage(croppedImage);
                    g2.DrawImage(pictureBox1.Image, 0, 0, new RectangleF(x, y, e.X - x, e.Y - y), GraphicsUnit.Pixel);

                    pictureBox1.Image = croppedImage;
                    bitmap = new Bitmap(croppedImage);
                    pictureBox1.Refresh();
                    userFunctionPointerSize.DynamicInvoke(pictureBox1.Image.Size);
                    
                    things.Clear();

                    tempThing = null;
                    redraw();
                    return;
                }

            Thing t = new Thing(controlId, x, y, e.X - x, e.Y - y, thingColor);

            if (controlId == idText)
            {
                tempThing = new Thing(idText, e.X, e.Y, 25, 15, thingColor);
                t = tempThing;
            }
            
            things.Add(t);
            if (controlId != idText)
                tempThing = null;
            redraw();
        }



        public void recalcSize()
        {
            if (bitmap == null || pictureBox1.Image == null) return;
            if (panel1.Width > pictureBox1.Width || panel1.Height > pictureBox1.Height)
            {
                foreach (Thing t in things)
                {
                    t.x += (panel1.Width - pictureBox1.Width) / 2;
                    t.y += (panel1.Height - pictureBox1.Height) / 2;
                }
                if (tempThing != null)
                {
                    tempThing.x += (panel1.Width - pictureBox1.Width) / 2;
                    tempThing.y += (panel1.Height - pictureBox1.Height) / 2;
                }
                Bitmap bitmapNew = new Bitmap(panel1.Width, panel1.Height);
                Graphics g1 = Graphics.FromImage(bitmapNew);
                g1.FillRectangle(new SolidBrush(Color.White), 0, 0, panel1.Width, panel1.Height);
                g1.DrawImage(bitmap, (panel1.Width - pictureBox1.Width) / 2, (panel1.Height - pictureBox1.Height) / 2);
                bitmap = bitmapNew;
                pictureBox1.Image = new Bitmap(bitmapNew);
                pictureBox1.Refresh();
            }       
            redraw();
        }

        /// <summary>
        /// Returns the image codec with the given mime type
        /// </summary>
        private static ImageCodecInfo GetEncoderInfo(string mimeType)
        {
            // Get image codecs for all image formats
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();

            // Find the correct image codec
            for (int i = 0; i < codecs.Length; i++)
                if (codecs[i].MimeType == mimeType)
                    return codecs[i];
            return null;
        } 

        public bool save()
        {

            long q = 87;
            // Encoder parameter for image quality
            EncoderParameter qualityParam =
                new EncoderParameter (System.Drawing.Imaging.Encoder.Quality , q);
            /*// Jpeg image codec
            ImageCodecInfo   jpegCodec = GetEncoderInfo("image/jpeg");*/
            //ImageCodecInfo pngCodex = GetEncoderInfo("image/png");

            EncoderParameters encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = qualityParam;
            try
            {
                //pictureBox1.Image.Save(nextFilename , jpegCodec, encoderParams);
                pictureBox1.Image.Save(nextFilename,ImageFormat.Png);
            }
            catch (Exception e)
            {
                MessageBox.Show("error saving image (maybe too big?) : \n " + e.Message);
                return false;
            }
            return true;
        }
 

        private void toolStripButtonUndo_Click(object sender, EventArgs e)
        {
            if (things.Count() > 0)
            {
                things.RemoveAt(things.Count() - 1); 
            }
            if (tempThing != null) tempThing = null;
            redraw();
        }

 

        public string getFilename()
        {
            return pictureBox1.ImageLocation;
        }

        private void ImageAnnotator_Resize(object sender, EventArgs e)
        {
            recalcSize();
        }

        private void panel1_Scroll(object sender, ScrollEventArgs e)
        {
            redraw();
        }

        private Image ResizeImg(Image img, int percentage)
        {
            //get the height and width of the image
            int originalW = img.Width;
            int originalH = img.Height;

            //get the new size based on the percentage change
            int resizedW = (int)(originalW * percentage)/100;
            int resizedH = (int)(originalH * percentage)/100;

            //create a new Bitmap the size of the new image
            Bitmap bmp = new Bitmap(resizedW, resizedH);
            //create a new graphic from the Bitmap
            Graphics graphic = Graphics.FromImage((Image)bmp);
            graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
            //draw the newly resized image
            graphic.DrawImage(img, 0, 0, resizedW, resizedH);
            //dispose and free up the resources
            graphic.Dispose();
            //return the image
            return (Image)bmp;
        }


        private void toolStripButtonResize_Click(object sender, EventArgs e)
        {
            ResizeForm rf = new ResizeForm();

            if (rf.ShowDialog() == DialogResult.OK)
                pictureBox1.Image = ResizeImg(pictureBox1.Image, rf.scale);
            else
                return;

            bitmap = new Bitmap(pictureBox1.Image);

            things.Clear();
            pictureBox1.Controls.Clear();
            pictureBox1.Refresh();
            tempThing = null;
            userFunctionPointerSize.DynamicInvoke(pictureBox1.Image.Size); 
        }

        private void toolStripButtonSaveAndClose_Click(object sender, EventArgs e)
        {
            if (save())
                userFunctionPointerClose.DynamicInvoke();
        }

        private void ImageAnnotator_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (tempThing != null && tempThing.id == idText)
            {
                switch (e.KeyChar)
                {
                    case (Char)Keys.Space:
                        tempThing.txt += " ";
                        break;
                    case (Char)Keys.Enter:
                        tempThing.txt += "\n\r";
                        break;
                    case (Char)Keys.Back:
                        tempThing.txt = tempThing.txt.Remove(tempThing.txt.Length - 1, 1);
                        break;
                    default:
                        tempThing.txt += e.KeyChar;
                        break;
                }
               
                Font stringFont = new Font("Arial", 9);
                SizeF stringSize = new SizeF();
                g = Graphics.FromImage(pictureBox1.Image);
                stringSize = g.MeasureString(tempThing.txt, stringFont);
                g.Dispose();
                tempThing.h = stringSize.Height + 8;
                tempThing.w = stringSize.Width + 8;
                redraw();
            }
        }

        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            Clipboard.SetImage(pictureBox1.Image);
        }



    }
    public class Thing
    {
        public float x, y, w, h;
        public int id;
        public string txt;
        public Color col;
        public Thing(int _id, float _x, float _y, float _w, float _h, Color _col)
        {
            id = _id;
            x = _x;
            y = _y;
            w = _w;
            h = _h;
            col = _col;
            txt = "";
        }

    }
}
